#include "gbBook/gbBook.h"

#include <time.h>

#include "glib-object.h"
#include "glib.h"
#include "glibconfig.h"

G_DEFINE_TYPE(GBBook, gb_book, G_TYPE_OBJECT);

#define GB_BOOK_GET_PRIVATE(OBJ) G_TYPE_INSTANCE_GET_PRIVATE(OBJ, GB_TYPE_BOOK, GBBookPrivate)

typedef struct _GBBookPrivate {
    GString *title;
    GString *author;
    GDate   *publish_date;
    guint    price;
} GBBookPrivate;

typedef enum {
    PROP_BOOK_0,
    PROP_BOOK_TITLE,
    PROP_BOOK_AUTHOR,
    PROP_BOOK_PUBLISH_DATE,
    PROP_BOOK_PRICE,
    N_PROPERTYS
} GB_BOOK_PROPERTY;

static void gb_book_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) {
    GBBook        *self = GB_BOOK(obj);
    GBBookPrivate *priv = GB_BOOK_GET_PRIVATE(self);
    switch (prop_id) {
        case PROP_BOOK_TITLE:
            g_value_set_string(value, priv->title->str);
            break;
        case PROP_BOOK_AUTHOR:
            g_value_set_string(value, priv->author->str);
            break;
        case PROP_BOOK_PUBLISH_DATE:
            g_value_set_boxed(value, priv->publish_date);
            break;
        case PROP_BOOK_PRICE:
            g_value_set_uint(value, priv->price);
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
            break;
    }
}

static void gb_book_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) {
    GBBook        *self = GB_BOOK(obj);
    GBBookPrivate *priv = GB_BOOK_GET_PRIVATE(self);
    switch (prop_id) {
        case PROP_BOOK_TITLE:
            if (priv->title)
                g_string_free(priv->title, TRUE);
            priv->title = g_string_new(g_value_get_string(value));
            break;
        case PROP_BOOK_AUTHOR:
            if (priv->author)
                g_string_free(priv->author, TRUE);
            priv->author = g_string_new(g_value_get_string(value));
            break;
        case PROP_BOOK_PUBLISH_DATE:
            if (!G_VALUE_HOLDS_BOXED(value)) {
                g_print("Property ID %d Set Value Is Not GDate Type", prop_id);
                break;
            }
            priv->publish_date = g_value_get_boxed(value);
            break;
        case PROP_BOOK_PRICE:
            priv->price = g_value_get_uint(value);
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
            break;
    }
}

static void gb_book_class_init(GBBookClass *klass) {
    g_type_class_add_private(klass, sizeof(GBBookPrivate));

    GObjectClass *base_class = G_OBJECT_CLASS(klass);
    base_class->set_property = gb_book_set_property;
    base_class->get_property = gb_book_get_property;

    GParamSpec *props[N_PROPERTYS] = {
        NULL,
    };
    props[PROP_BOOK_TITLE]  = g_param_spec_string("title", "Title", "Book Title", "UNKNOW", G_PARAM_READWRITE);
    props[PROP_BOOK_AUTHOR] = g_param_spec_string("author", "Author", "Book Author", "UNKNOW", G_PARAM_READWRITE);
    props[PROP_BOOK_PUBLISH_DATE] =
        g_param_spec_boxed("publish_date", "PublishDate", "Book Publish Date", G_TYPE_DATE, G_PARAM_READWRITE);
    props[PROP_BOOK_PRICE] = g_param_spec_uint("price", "Price", "Book Price", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
    g_object_class_install_properties(base_class, N_PROPERTYS, props);
}

static void gb_book_init(GBBook *self) {
    // 实例属性初始化
    GBBookPrivate *priv = GB_BOOK_GET_PRIVATE(self);
    priv->publish_date  = g_date_new();
}

void gb_book_info_print(GBBook *book) {
    gchar *title  = NULL;
    gchar *author = NULL;
    GDate *p_date = NULL;
    guint  price  = 0;

    g_object_get(G_OBJECT(book), "title", &title, "author", &author, "publish_date", &p_date, "price", &price, NULL);
    GString *date_string = g_string_new_len(NULL, 16);
    g_date_strftime(date_string->str, 16, "%Y-%m-%d", p_date);

    g_print("Title: %s\n"
            "   Author: %s\n"
            "   Publish Date: %s\n"
            "   Price: %d\n",
            title,
            author,
            date_string->str,
            price);

    g_free(title);
    g_free(author);
    g_free(p_date);
}